/**
 * 
 */
package com.csc.bankingsystem.web.controller;

import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.csc.bankingsystem.core.dto.AccountInfoDTO;
import com.csc.bankingsystem.core.dto.BSAPIReturnObject;
import com.csc.bankingsystem.core.dto.BSPaging;
import com.csc.bankingsystem.core.dto.TransferMoneyDTO;
import com.csc.bankingsystem.core.dto.TransferRecordDTO;
import com.csc.bankingsystem.core.entity.AccountInfo;
import com.csc.bankingsystem.core.entity.TransferRecord;
import com.csc.bankingsystem.core.entity.UserInfo;
import com.csc.bankingsystem.core.exception.NotEnoughMoneyException;
import com.csc.bankingsystem.core.service.AccountInfoService;
import com.csc.bankingsystem.core.service.TransferRecordService;
import com.csc.bankingsystem.core.service.UserInfoService;
import com.csc.bankingsystem.core.util.ConstantManager;
import com.csc.bankingsystem.core.util.EncryptionUtil;
import com.csc.bankingsystem.web.exception.BSAPIResponseException;
import com.csc.bankingsystem.web.util.BSDateTimeUtil;

/**
 * @author angelkid
 *
 */
@Controller
public class MainAPIController extends BSAbstractController {
	private static final Logger logger = LoggerFactory
			.getLogger(HomeController.class);

	@Autowired
	AccountInfoService accountInfoService;

	@Autowired
	TransferRecordService transferRecordService;

	@Autowired
	UserInfoService userInfoService;

	/**
	 * Get Account Info of Current UserInfo
	 * 
	 * @param request
	 * @return List<AccountInfoDTO> list AccountInfo of currentUser
	 */
	@RequestMapping(value = "/api/get-account-info", method = RequestMethod.POST)
	@ResponseBody
	public List<AccountInfoDTO> getAccountInfo(HttpServletRequest request) {
		UserInfo userInfo = getCurrentUser(request);
		List<AccountInfoDTO> accountInfoDTOs = new ArrayList<AccountInfoDTO>();
		List<AccountInfo> accountInfos = accountInfoService
				.findAllByUserInfoId(userInfo.getId());

		if (accountInfos != null) {
			for (AccountInfo accountInfo : accountInfos) {
				AccountInfoDTO accountInfoDTO = new AccountInfoDTO(accountInfo);
				accountInfoDTOs.add(accountInfoDTO);
			}
		}
		return accountInfoDTOs;

	}

	/**
	 * Get Transfer Record of current user
	 * 
	 * @param request
	 * @param firstResult
	 *            first item index
	 * @param pageSize
	 *            page size
	 * @param datetimeStr
	 *            string of lasted datetime (of transfer record) that client
	 *            already receive
	 * @return
	 */
	@RequestMapping(value = "/api/get-transfer-records", method = RequestMethod.POST)
	@ResponseBody
	public List<TransferRecordDTO> getTransferRecords(
			HttpServletRequest request, @RequestParam Integer firstResult,
			@RequestParam Integer pageSize,
			@RequestParam(required = false) String datetimeStr) {
		UserInfo userInfo = getCurrentUser(request);
		BSPaging paging = new BSPaging(firstResult, pageSize);
		Date toDate = BSDateTimeUtil.parseDateFromString(datetimeStr,
				ConstantManager.FULL_DATETIME_PATTERN);
		List<TransferRecord> transferRecords = transferRecordService
				.findByUserInfo(paging, userInfo, toDate);
		List<TransferRecordDTO> transferRecordDTOs = new ArrayList<TransferRecordDTO>();
		if (transferRecords != null) {
			for (TransferRecord transferRecord : transferRecords) {
				TransferRecordDTO transferRecordDTO = new TransferRecordDTO(
						transferRecord);
				transferRecordDTOs.add(transferRecordDTO);
			}
		}

		return transferRecordDTOs;

	}

	/**
	 * Change password of Client
	 * 
	 * @param request
	 * @param response
	 * @param oldPassword
	 *            old password
	 * @param newPassword
	 *            new password
	 * @return
	 */
	@RequestMapping(value = "/api/change-password", method = RequestMethod.POST)
	@ResponseBody
	public BSAPIReturnObject changePassword(HttpServletRequest request,
			HttpServletResponse response, @RequestParam String oldPassword,
			@RequestParam String newPassword) {
		UserInfo userInfo = getCurrentUser(request);
		try {
			oldPassword = EncryptionUtil.generateMD5HashFromString(oldPassword
					+ userInfo.getSalt());
			newPassword = EncryptionUtil.generateMD5HashFromString(newPassword
					+ userInfo.getSalt());
		} catch (NoSuchAlgorithmException e) {
			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			throw new BSAPIResponseException(
					ConstantManager.INTERNAL_SERVER_ERROR,
					"Internal server error");
		}
		if (userInfo.getPassword().compareTo(oldPassword) == 0) {
			userInfoService.changePassword(userInfo, newPassword);
			return new BSAPIReturnObject(ConstantManager.SUCCESS, null);
		} else {
			throw new BSAPIResponseException(
					ConstantManager.INVALID_OLD_PASSWORD,
					"Old password is invalid");
		}
	}

	/**
	 * Transfer money
	 * 
	 * @param request
	 * @param response
	 * @param sourceAccountNumber
	 *            source account number
	 * @param destinationAccountNumber
	 *            destination account number
	 * @param amount
	 *            amount money
	 * @param message
	 *            message
	 * @return
	 */
	@RequestMapping(value = "/api/transfer-money", method = RequestMethod.POST)
	@ResponseBody
	public TransferMoneyDTO transferMoney(HttpServletRequest request,
			HttpServletResponse response,
			@RequestParam String sourceAccountNumber,
			@RequestParam String destinationAccountNumber,
			@RequestParam Long amount, @RequestParam String message) {
		UserInfo userInfo = getCurrentUser(request);
		AccountInfo source = accountInfoService
				.findByAccountNumber(sourceAccountNumber);
		AccountInfo destination = accountInfoService
				.findByAccountNumber(destinationAccountNumber);

		
		if(source == null){
			throw new BSAPIResponseException(
					ConstantManager.TRANSFER_INVALID_SOURCE,
					"Invalid source account number");
		} else if(destination == null){
			throw new BSAPIResponseException(
					ConstantManager.TRANSFER_INVALID_DESTINATION,
					"Invalid destination account number");
		}
		
		boolean hasPermission = false;
		for (AccountInfo accountInfo : userInfo.getAccountInfos()) {
			if (accountInfo.getId() == source.getId()) {
				hasPermission = true;
				break;
			}
		}
		if (!hasPermission) {
			throw new BSAPIResponseException(
					ConstantManager.TRANSFER_DO_NOT_HAVE_PERMISSION,
					"Do not have permission to transfer money from source account number.");
		}
		try {
			transferRecordService.createTransferRecord(userInfo, source,
					destination, amount, message);
			List<AccountInfoDTO> accountInfoDTOs = new ArrayList<AccountInfoDTO>();
			userInfo = userInfoService.find(userInfo.getId());
			if (userInfo.getAccountInfos() != null) {
				for (AccountInfo accountInfo : userInfo.getAccountInfos()) {
					AccountInfoDTO accountInfoDTO = new AccountInfoDTO(accountInfo);
					accountInfoDTOs.add(accountInfoDTO);
				}
			}
			return new TransferMoneyDTO(ConstantManager.SUCCESS, null,accountInfoDTOs);
		} catch (NotEnoughMoneyException e) {
			throw new BSAPIResponseException(e.getErrorCode(), e.getMessage());
		}
	}
}
